In [1]:
from sympy import *
Vamos a decirle a sympy que renderice las salidas en LaTeX con mathjax
In [2]:
init_printing()
Ejemplo Si es cierto $a\to b$, ¿podemos inferir algo sobre $a\vee c\to b\vee c$? ¿Y sobre $a\wedge c\to b\wedge c$?
In [3]:
a,b,c = symbols("a,b,c")
Vamos a usar que $\Gamma \models \alpha$ equivale a que $\Gamma\cup\{\neg \alpha\}$ es insatisfacible
In [4]:
p = a >> b
q = (a|c)>>(b|c)
In [5]:
p&~q
Out[5]:
In [6]:
satisfiable(p&~q)
Out[6]:
También podíamos ver si $p\to q$ es una tautología por el teorema de la deducción, o lo que es lo mismo, mostrar que siempre es verdadera
In [7]:
simplify(p>>q)
Out[7]:
Lo mismo con $a\wedge c\to b\wedge c$
In [8]:
simplify(p>>((a&c)>>(b&c)))
Out[8]:
Ejemplo Estudia si el conjunto $\{c\to (a\vee c), b\to(c\to a), d\wedge \neg (c\to a)\}$ es satisfacible
In [9]:
a,b,c,d = symbols("a,b,c,d")
In [10]:
satisfiable((c>>(a|b))&(b>>(c>>a))&d&~(c>>a))
Out[10]:
De ser satisfacible, nos daría un mundo donde lo es
In [11]:
satisfiable(a|b)
Out[11]:
O bien todos los mundos donde lo es, si así se lo pedimos
In [12]:
list(satisfiable(a|b, all_models=True))
Out[12]:
Podemos detectar si $\Gamma\models a$ con la siguiente función
In [13]:
def implica(gamma,a):
""" Determina si Gamma implica semanticamente a"""
p = True
for x in gamma:
p = p & x
p=p&(~a)
return not(satisfiable(p))
In [14]:
implica([a,a>>b],b)
Out[14]:
In [15]:
implica([a>>b,b>>c],a>>c)
Out[15]:
Volvamos al primer ejemplo
In [16]:
implica([a>>b], (a|c)>>(b|c))
Out[16]:
Otro ejemplo
In [17]:
implica([a>>b],a)
Out[17]:
Una proposición $a$ es tautología si $\models a$
In [18]:
implica([], a>>(b>>a))
Out[18]:
In [19]:
implica([],~a>>(a>>b))
Out[19]:
In [20]:
def tautologia(a):
return not(satisfiable(~a))
In [21]:
tautologia(~a>>(a>>b))
Out[21]:
In [22]:
tautologia(a>>b)
Out[22]:
In [23]:
(a>>b).args
Out[23]:
In [24]:
to_cnf(a>>(b&c))
Out[24]:
In [25]:
type(_)
Out[25]:
In [26]:
to_cnf(a>>b)
Out[26]:
In [27]:
type(_)
Out[27]:
Vamos a definir la forma clausular de un conjunto de proposiciones
In [28]:
G= {c>>(a|b), b>>(c>>a), d&~(c>>a)}
In [29]:
def forma_clausular(G):
fc = set([])
for g in G:
q = to_cnf(g)
if isinstance(q,And):
fc.update(q.args)
else:
fc.add(q)
return fc
In [30]:
forma_clausular(G)
Out[30]:
In [31]:
def es_literal(p):
if isinstance(p,Symbol):
return True
if isinstance(p,Not):
ar = p.args
if len(ar)==1 and isinstance(ar[0],Symbol):
return True
return False
In [32]:
es_literal(a)
Out[32]:
In [33]:
es_literal(~a)
Out[33]:
In [34]:
es_literal(a&b)
Out[34]:
In [35]:
v=Or(*[])
In [36]:
type(v)
Out[36]:
In [37]:
v==False
Out[37]:
In [38]:
def es_clausula(p):
if p==False:
return True
if es_literal(p):
return True
if isinstance(p,Or):
ar = p.args
return all(es_literal(a) for a in ar)
return False
In [39]:
es_clausula(a&b)
Out[39]:
In [40]:
es_clausula(a|~b)
Out[40]:
In [41]:
def complemento(p):
if isinstance(p,Symbol):
return ~p
if isinstance(p,Not):
return p.args[0]
return None
In [42]:
complemento(a)
Out[42]:
In [43]:
complemento(~a)
Out[43]:
In [44]:
l=[1,2,3]
l.remove(3)
2 in l
Out[44]:
In [45]:
def quita(c,l):
if c==False:
return False
if es_literal(c):
if c==l:
return False
return c
ar = list(c.args)
if l in ar:
ar.remove(l)
return Or(*ar)
return c
In [46]:
quita(a|b,b)
Out[46]:
In [47]:
quita(a|b,~b)
Out[47]:
In [48]:
def primero(xs,cond):
for x in xs:
if cond(x):
return x
return None
In [49]:
primero([1,2,3],lambda x:(x%2)==0)
Out[49]:
In [50]:
def clausula_a_lista(c):
if c==False:
return []
if es_literal(c):
return [c]
return list(c.args)
In [51]:
clausula_a_lista(a|b|~c)
Out[51]:
In [52]:
def clausula_unit(cs):
l = primero(cs,es_literal)
if l!=None:
print("Hemos encontrado una clausula unit ", l)
csp = [c for c in cs if not(l in clausula_a_lista(c))]
cspq = [quita(c,complemento(l)) for c in csp]
pprint(cspq)
return cspq
return cs
In [53]:
clausula_unit([a,a|b, ~a|c, c|b])
Out[53]:
In [54]:
Or(a,~a,b)
Out[54]:
In [55]:
simplify(_)
Out[55]:
In [56]:
def quita_tautologias(cs):
return [c for c in cs if simplify(c)!=True]
In [57]:
quita_tautologias([a|b|~a,a|c])
Out[57]:
In [58]:
l=[1,2]
La siguiente función determina si todos los literales de una cláusula están contenidos en otra cláusula
In [59]:
def contenido(c1,c2):
return all((l in clausula_a_lista(c2)) for l in clausula_a_lista(c1))
In [60]:
contenido(a|b,a|b|c)
Out[60]:
In [61]:
contenido(a|b,a|c)
Out[61]:
In [62]:
def quita_redundantes(cs):
return [c for c in cs if not(any(contenido(d,c) and d!=c for d in cs))]
In [63]:
quita_redundantes([a|b,a|b|c])
Out[63]:
In [66]:
def literal_puro(cs):
literales = set([])
for c in cs:
literales.update(clausula_a_lista(c))
l = primero(literales, lambda x:not(complemento(x) in literales))
if l==None:
return cs
print("Hemos encontrado un literal puro", l)
csp = [c for c in cs if not(l in clausula_a_lista(c))]
print(csp)
return csp
In [67]:
literal_puro([a|b,~a])
Out[67]:
In [68]:
def divide(cs):
literales = set([])
for c in cs:
literales.update(clausula_a_lista(c))
l = primero(literales, lambda x:(complemento(x) in literales))
cl = complemento(l)
csl = set([quita(c,l) for c in cs if l in clausula_a_lista(c)])
csnl= set([quita(c,cl) for c in cs if cl in clausula_a_lista(c)])
csc = set([c for c in cs if not(l in clausula_a_lista(c)) and not(cl in clausula_a_lista(c))])
parte1=csl.union(csc)
parte2=csnl.union(csc)
print("Dividimos usando ",l)
pprint(parte1)
pprint(parte2)
return [csl.union(csc),csnl.union(csc)]
In [69]:
divide([a|b,~a|b,a|c, b|c])
Out[69]:
In [70]:
def inconsistente(cs):
"""Detecta si cs es satisfacible usando Davis-Putnam"""
css = quita_tautologias(quita_redundantes(cs))
unitpuro=True
while unitpuro:
cssn = clausula_unit(css)
if cssn== css:
cssn = literal_puro(css)
if cssn==css:
unitpuro=False
if len(cssn)==0:
print("Hemos llegado al conjunto vacío")
return False
if False in cssn:
print("Hemos encontrado la cláusula vacía")
return True
css=cssn
d = divide(css)
return inconsistente(d[0]) and inconsistente(d[1])
In [71]:
inconsistente([~a,a])
Out[71]:
In [72]:
clausula_unit([a,b])
Out[72]:
In [73]:
clausula_unit([b])
Out[73]:
In [74]:
inconsistente([a,b])
Out[74]:
In [75]:
inconsistente([~a|b,a,~b])
Out[75]:
Este último ejemplo es precisamente el modus ponens
In [76]:
inconsistente(forma_clausular([a>>b,a,~b]))
Out[76]:
Ejemplo Veamos que $(a\to (b\to c))\to(\neg (a\to \neg b)\to c)$ es una tautología. Esto equivale a probar $$\models (a\to (b\to c))\to(\neg (a\to \neg b)\to c).$$ Por el Teorema de la deducción (dos veces), basta probar que $$\{a\to (b\to c), \neg (a\to \neg b)\}\models c,$$ y esto es equivalente a demostrar que el conjunto $$\{a\to (b\to c), \neg (a\to \neg b),\neg c\}$$ es insatisfacible
In [77]:
forma_clausular([a>>(b>>c), ~(a>>~b), ~c])
Out[77]:
In [78]:
inconsistente(_)
Out[78]:
Ejemplo Veamos que $$\{(a\to \neg b\vee d)\wedge(b\wedge \neg d\to a\vee c), (d\to (a \leftrightarrow \neg b)\vee (b\wedge \neg c)\}\models (\neg b\to (d\wedge (c\vee \neg d)))\to c\wedge d$$
In [79]:
Gamma={(a>>(~b|d))&((b&~d)>>(a|c)), (d>>(Equivalent(a,~b))|(b|~c)),
(~b>>(d&(c|~d))),~(c&d)}
In [80]:
forma_clausular(Gamma)
Out[80]:
In [81]:
inconsistente(forma_clausular(Gamma))
Out[81]:
In [82]:
Or(Or(a,b),Or(a,c))
Out[82]:
In [83]:
def resolventes(a,b):
literales=[l for l in clausula_a_lista(a) if complemento(l) in clausula_a_lista(b)]
return [Or(quita(a,l),quita(b,complemento(l))) for l in literales]
In [84]:
resolventes(~a|b,a|~b)
Out[84]: